/**
 * 
 */
package edu.gtcfla.km.admin.appmodules.service.impl;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.vsg.biz.BusinessEntityFactory;
import org.vsg.biz.BusinessOperationException;
import org.vsg.biz.account.Menu;
import org.vsg.biz.account.Module;
import org.vsg.biz.account.Permission;
import org.vsg.biz.account.Role;
import org.vsg.biz.account.repositories.MenuRepo;
import org.vsg.biz.account.repositories.ModuleFunctionRepo;
import org.vsg.biz.account.repositories.ModuleRepo;
import org.vsg.biz.account.repositories.PermissionRepo;
import org.vsg.biz.account.repositories.ResourceRepo;
import org.vsg.biz.account.repositories.RoleRepo;
import org.vsg.biz.account.repositories.UrlRepo;

import edu.gtcfla.km.admin.appmodules.service.MenuManagerService;
import edu.gtcfla.km.admin.appmodules.vo.HierarchyMenuItem;
import edu.gtcfla.km.admin.appmodules.vo.HierarchyMenuResourcePermissionVO;
import edu.gtcfla.km.admin.appmodules.vo.HierarchyMenuResourceWithCheckedView;
import edu.gtcfla.km.admin.appmodules.vo.MenuFormView;
import edu.gtcfla.km.admin.appmodules.vo.MenuWithMethodVO;

/**
 * @author Bill Vison
 *
 */
public class MenuResourceManagerServiceImpl implements
		MenuManagerService {

	@Resource
	private ModuleFunctionRepo moduleFunctionRepo;
	
	@Resource
	private MenuRepo<Menu , String> menuRepo;
	
	@Resource
	private ModuleRepo<Module , String> moduleRepo;
	
	@Resource
	private UrlRepo urlRepo;
	
	@Resource
	private PermissionRepo permissionRepo;
	
	
	@Resource
	private RoleRepo roleRepo;

	@Autowired
	private ResourceRepo resourceRepo;
	
	@Autowired
	private BusinessEntityFactory entityFactory;	
	
	@Override
	public Set<HierarchyMenuItem> buildHierachyMenuResourceView()
			throws BusinessOperationException {
		
		Iterable<Menu> menuSet =  menuRepo.findAll();
		
		Map<String, HierarchyMenuItem> permMenus = new LinkedHashMap<String,HierarchyMenuItem>();

		Set<Menu> menuWithParentSet = new LinkedHashSet<Menu>();
		
		Set<String> moduleIds = new LinkedHashSet<String>();
		
		for (Menu menu : menuSet ) {
			
			HierarchyMenuItem menuItem = null;
			

			if (menu.getParent() != null) {
				
				HierarchyMenuResourceWithCheckedView hieraMenu = new HierarchyMenuResourceWithCheckedView();
				hieraMenu.setId( menu.getId() );
				hieraMenu.setText( menu.getName() );
				hieraMenu.setMenuLevel( menu.getMenuLevel() );
				hieraMenu.setLeaf(true);
				hieraMenu.setSortNum( menu.getSortNum() );
				hieraMenu.setDisabled( menu.getDisabled() );
				
				menuItem = hieraMenu;			
				
				menuWithParentSet.add( menu );
			} else {

				menuItem = new HierarchyMenuItem();
				menuItem.setId( menu.getId() );
				menuItem.setText( menu.getName() );
				menuItem.setMenuLevel( menu.getMenuLevel() );
				menuItem.setLeaf(true);
				menuItem.setSortNum( menu.getSortNum() );	
				menuItem.setDisabled( menu.getDisabled() );

			}
			
			moduleIds.add( menu.getModuleId() );
			
			permMenus.put( menu.getId()  , menuItem);
		}
		
		// --- find module by ides ---
		Map<String, Module> moduleMap =  moduleRepo.findAllByIds( moduleIds );
		
		for (Menu menu : menuSet ) {
			HierarchyMenuItem hieraMenu = permMenus.get(menu.getId());
			Module mod = moduleMap.get( menu.getModuleId() );
			hieraMenu.setModuleName( mod.getName() );
		}

		
		Set<String> dupicateNodeIds = new LinkedHashSet<String>();
		
		// --- with parent ---
		for (Menu menu : menuWithParentSet) {
			String parentId = menu.getParent().getId();

			HierarchyMenuItem treeNode = permMenus.get( parentId );
			if (treeNode == null) {
				throw new BusinessOperationException("Found Parent Tree Node is null");
			}
			treeNode.setLeaf(false);
			
			Set<HierarchyMenuItem> children = treeNode.getChildren();
			if ( children == null) {
				children = new LinkedHashSet<HierarchyMenuItem>();
			}
			
			// --- get child node 
			HierarchyMenuItem child = permMenus.get( menu.getId() );
			
			// ---- dupicate node ---
			dupicateNodeIds.add( child.getId() );
			
			children.add( child );
			// --- set tree node ---
			treeNode.setChildren( children );
		}		

		for (String nodeId : dupicateNodeIds) {
			permMenus.remove( nodeId );
		}

		Set<HierarchyMenuItem> result = new LinkedHashSet<HierarchyMenuItem>();
		
		result.addAll( sortAllMenu(permMenus.values()) );		

		return result;
	}

	private List<HierarchyMenuItem> sortAllMenu(Collection<HierarchyMenuItem> sortOrder) {
		List<HierarchyMenuItem> newList = new ArrayList<HierarchyMenuItem>( sortOrder);
		Collections.sort(newList, new HierarchyMenuItemSorter() );
		
		for (HierarchyMenuItem item : newList) {
			if (!item.getChildren().isEmpty() ) {
				sortAllMenu( item.getChildren() );
			}
		}
		
		return newList;
	}
	
	

	@Override
	public void authMenusWithUnderRes(String roleId,
			Set<HierarchyMenuResourcePermissionVO> perms)
			throws BusinessOperationException {
		// TODO Auto-generated method stub
		
		Role role = this.roleRepo.findOne( roleId );
		
		Set<Permission> permSet = new LinkedHashSet<Permission>();
		
		Set<String> menuIds = new LinkedHashSet<String>();
		
		for (HierarchyMenuResourcePermissionVO permItem : perms ) {
			
			org.vsg.biz.account.Resource res = resourceRepo.findOne( permItem.getId() );
			
			Permission perm = entityFactory.create( Permission.class );
			
			perm.setAccessControl( permItem.getAcl() );
			perm.setAssignedRole( role );
			perm.setAssignedResource( res );
			permSet.add( perm );
			
			menuIds.add( permItem.getId() );
			
		}
		
		permissionRepo.removePermissionForMenu(roleId, menuIds);

		
		// --- add new permission ---
		permissionRepo.saveAll( permSet );	
		
	}


	
	
	@Override
	public void saveNewMenuFromView(MenuFormView view) throws BusinessOperationException {
		// TODO Auto-generated method stub
		
		Menu menu = view.getMenu();
		
		// --- set parent ---
		String parentId = view.getParentId();
		if (parentId != null && !parentId.trim().equals("") && !parentId.trim().equals("root")) {
			Menu parent = menuRepo.findOne( parentId );
			menu.setParent( parent );
		} else {
			menu.setParent(null);
		}
		
		StringBuilder idGen = new StringBuilder("mnu:");
		
		String moduleId = view.getModuleId();
		if (moduleId != null && !moduleId.trim().equals("")) {
			String[] moduleArray = moduleId.split(":");
			for (int i = 1 ; i < moduleArray.length ; i++) {
				if (i != 1) {
					idGen.append(":");
				}
				idGen.append(moduleArray[i]);
			}
			menu.setModuleId(moduleId);
		}
		
		if (view.getCode() == null || view.getCode().equals("")) {
			menu.setCode( null );
		} else {
			idGen.append(":"+menu.getCode());
		}
		menu.setId( idGen.toString() );
		
		
		
		this.menuRepo.save(menu);
		
	}


	@Override
	public void mergeAllMenuMethod(Set<MenuWithMethodVO> menuMethodSet)
			throws BusinessOperationException {
		// TODO Auto-generated method stub
		
	}


	@Override
	public Set<HierarchyMenuResourcePermissionVO> buildHierachyMenuPermissionByRoleId(
			String roleId) throws BusinessOperationException {
		
		Map<String, Menu>  menuMap =  menuRepo.findAvailableMap();
		
		Map<String, HierarchyMenuResourcePermissionVO> permMenus = new LinkedHashMap<String,HierarchyMenuResourcePermissionVO>();
		
		Map<String , Permission> permMap = permissionRepo.findMenuPermissionMapByRoleId(roleId , menuMap.keySet() );

		Set<Menu> menuWithParentSet = new LinkedHashSet<Menu>();
		
		for (Menu menu : menuMap.values() ) {
			HierarchyMenuResourcePermissionVO hieraMenu = new HierarchyMenuResourcePermissionVO();
			hieraMenu.setId( menu.getId() );
			hieraMenu.setText( menu.getName() );
			hieraMenu.setMenuLevel( menu.getMenuLevel() );
			hieraMenu.setLeaf(true);
			
			Permission perm = permMap.get(menu.getId());
			if (perm != null) {
				if (perm.getAccessControl() != null) {
					hieraMenu.setAcl( perm.getAccessControl() );
				} else {
					hieraMenu.setAcl( perm.getBindingActionId() );
				}
			}
			if (hieraMenu.getAcl() == null) {
				hieraMenu.setAcl("");
			}
			
			
			if (menu.getParent() != null) {
				menuWithParentSet.add( menu );
			}
			
			permMenus.put( menu.getId()  , hieraMenu);
		}

		
		Set<String> dupicateNodeIds = new LinkedHashSet<String>();
		
		// --- with parent ---
		for (Menu menu : menuWithParentSet) {
			String parentId = menu.getParent().getId();

			HierarchyMenuResourcePermissionVO treeNode = permMenus.get( parentId );
			if (treeNode == null) {
				throw new BusinessOperationException("Found Parent Tree Node is null");
			}
			treeNode.setLeaf(false);
			Set<HierarchyMenuResourcePermissionVO> children = treeNode.getChildren();
			if ( children == null) {
				children = new LinkedHashSet<HierarchyMenuResourcePermissionVO>();
			}
			
			// --- get child node 
			HierarchyMenuResourcePermissionVO child = permMenus.get( menu.getId() );
			
			// ---- dupicate node ---
			dupicateNodeIds.add( child.getId() );
			
			children.add( child );
			// --- set tree node ---
			treeNode.setChildren( children );
		}		

		for (String nodeId : dupicateNodeIds) {
			permMenus.remove( nodeId );
		}		

		Set<HierarchyMenuResourcePermissionVO> result = new LinkedHashSet<HierarchyMenuResourcePermissionVO>();
		
		result.addAll( permMenus.values() );		

		return result;
	}


	@Override
	public Set<HierarchyMenuItem> getMenuTreeInFunction(String functionId)
			throws BusinessOperationException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<HierarchyMenuItem> selAllMenuTreeNode()
			throws BusinessOperationException {

		
		Iterable<Menu> menuSet =  menuRepo.findAll();
		
		Map<String, HierarchyMenuItem> permMenus = new LinkedHashMap<String,HierarchyMenuItem>();

		Set<Menu> menuWithParentSet = new LinkedHashSet<Menu>();
		
		Set<String> moduleIds = new LinkedHashSet<String>();
		
		for (Menu menu : menuSet ) {
			
			HierarchyMenuItem menuItem = null;
			

			if (menu.getParent() != null) {
				
				HierarchyMenuItem hieraMenu = new HierarchyMenuItem();
				hieraMenu.setId( menu.getId() );
				hieraMenu.setText( menu.getName() );
				hieraMenu.setMenuLevel( menu.getMenuLevel() );
				hieraMenu.setLeaf(true);
				hieraMenu.setSortNum( menu.getSortNum() );
				hieraMenu.setDisabled( menu.getDisabled() );
				
				menuItem = hieraMenu;			
				
				menuWithParentSet.add( menu );
			} else {

				menuItem = new HierarchyMenuItem();
				menuItem.setId( menu.getId() );
				menuItem.setText( menu.getName() );
				menuItem.setMenuLevel( menu.getMenuLevel() );
				menuItem.setLeaf(true);
				menuItem.setSortNum( menu.getSortNum() );	
				menuItem.setDisabled( menu.getDisabled() );

			}
			
			moduleIds.add( menu.getModuleId() );
			
			permMenus.put( menu.getId()  , menuItem);
		}
		
		// --- find module by ides ---
		Map<String, Module> moduleMap =  moduleRepo.findAllByIds( moduleIds );
		
		for (Menu menu : menuSet ) {
			HierarchyMenuItem hieraMenu = permMenus.get(menu.getId());
			Module mod = moduleMap.get( menu.getModuleId() );
			hieraMenu.setModuleName( mod.getName() );
		}

		
		Set<String> dupicateNodeIds = new LinkedHashSet<String>();
		
		// --- with parent ---
		for (Menu menu : menuWithParentSet) {
			String parentId = menu.getParent().getId();

			HierarchyMenuItem treeNode = permMenus.get( parentId );
			if (treeNode == null) {
				throw new BusinessOperationException("Found Parent Tree Node is null");
			}
			treeNode.setLeaf(false);
			
			Set<HierarchyMenuItem> children = treeNode.getChildren();
			if ( children == null) {
				children = new LinkedHashSet<HierarchyMenuItem>();
			}
			
			// --- get child node 
			HierarchyMenuItem child = permMenus.get( menu.getId() );
			
			// ---- dupicate node ---
			dupicateNodeIds.add( child.getId() );
			
			children.add( child );
			// --- set tree node ---
			treeNode.setChildren( children );
		}		

		for (String nodeId : dupicateNodeIds) {
			permMenus.remove( nodeId );
		}

		Set<HierarchyMenuItem> result = new LinkedHashSet<HierarchyMenuItem>();
		
		result.addAll( sortAllMenu(permMenus.values()) );		

		return result;
	}	
	

	
	
}
